15 Glide 图片变换功能

项目地址:https://github.com/bumptech/glide
官方文档:http://bumptech.github.io/glide/
中文文档:https://muyangmin.github.io/glide-docs-cn/
原文链接:https://blog.csdn.net/guolin_blog/article/details/71524668
·
文章目录:
Glide 快速入门
Glide 源码分析执行流程
Glide 缓存机制
Glide 回调与监听
Glide 图片变换功能

一个问题

在正式开始学习 Glide 的图片变化功能之前,我们先来看一个问题,这个问题可能有不少人都在使用 Glide 的时候都遇到过,正好在本篇内容的主题之下我们顺带着将这个问题给解决了。

首先我们尝试使用 Glide 来加载一张图片,图片 URL 地址是:

1
https://www.baidu.com/img/bd_logo1.png

这是百度首页 logo 的一张图片,图片尺寸是 540 * 258 像素。

接下来我们编写一个非常简单的布局文件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Load Image"
android:onClick="loadImage" />
<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

布局文件中只有一个按钮和一个用于显示图片的 ImageView。注意,ImageView 的宽和高这里设置的都是 wrap_content。

然后编写如下的代码来加载图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainActivity extends AppCompatActivity {
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_view);
}
public void loadImage(View view) {
String url = "https://www.baidu.com/img/bd_logo1.png";
Glide.with(this)
.load(url)
.into(imageView);
}
}

现在运行一下程序并点击加载图片按钮,效果如下图所示。

图片是正常加载出来了,不过大家有没有发现一个问题。百度这张 logo 图片的尺寸只有 540* 258 像素,但是我的手机的分辨率却是 1080 * 1920 像素,而我们将 ImageView 的宽高设置的都是 wrap_content,那么图片的宽度应该只有手机屏幕宽度的一半而已,但是这里却充满了全屏,这是为什么呢?

如果你之前也被这个问题困扰过,那么恭喜,本篇文章正是你所需要的。之所以会出现这个现象,就是因为 Glide 的图片变换功能所导致的。那么接下来我们会先分析如何解决这个问题,然后再深入学习 Glide 图片变化的更多功能。

稍微对 Android 有点了解的人应该都知道 ImageView 有 scaleType 这个属性,但是可能大多数人却不知道,如果在没有指定 scaleType 属性的情况下,ImageView 默认的 scaleType 是什么?

这个问题如果直接问我,我也答不上来。不过动手才是检验真理的唯一标准,想知道答案,自己动手试一下就知道了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_view);
Log.d(TAG, "imageView scaleType is " + imageView.getScaleType());
}
...
}

可以看到,我们在 onCreate() 方法中打印了 ImageView 默认的 scaleType,然后重新运行一下程序,结果如下图所示:

由此我们可以得知,在没有明确指定的情况下,ImageView 默认的 scaleType 是 FIT_CENTER。

有了这个前提条件,我们就可以继续去分析 Glide 的源码了。回顾一下第二篇文章中我们分析过的 into() 方法,它是在 GenericRequestBuilder 类当中的,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}

还记得我们当初分析这段代码的时候,直接跳过前面的所有代码,直奔最后一行。因为那个时候我们的主要任务是分析 Glide 的主线执行流程,而不去仔细阅读它的细节,但是现在我们是时候应该阅读一下细节了。

可以看到,这里在第 7 行会进行一个 switch 判断,如果 ImageView 的 scaleType 是 CENTER_CROP,则会去调用 applyCenterCrop() 方法,如果 scaleType 是 FIT_CENTER、FIT_START 或 FIT_END,则会去调用 applyFitCenter() 方法。这里的 applyCenterCrop() 和 applyFitCenter() 方法其实就是向 Glide 的加载流程中添加了一个图片变换操作,具体的源码我们就不跟进去看了。

那么现在我们就基本清楚了,由于 ImageView 默认的 scaleType 是 FIT_CENTER,因此会自动添加一个 FitCenter 的图片变换,而在这个图片变换过程中做了某些操作,导致图片充满了全屏。

那么我们该如何解决这个问题呢?最直白的一种办法就是看着源码来改。当 ImageView 的 scaleType 是 CENTER_CROP、FIT_CENTER、FIT_START 或 FIT_END 时不是会自动添加一个图片变换操作吗?那我们把 scaleType 改成其他值不就可以了。ImageView 的 scaleType 可选值还有 CENTER、CENTER_INSIDE、FIT_XY 等。这当然是一种解决方案,不过只能说是一种比较笨的解决方案,因为我们为了解决这个问题而去改动了 ImageView 原有的 scaleType,那如果你真的需要 ImageView 的 scaleType 为 CENTER_CROP 或 FIT_CENTER 时可能就傻眼了。

上面只是我们通过分析源码得到的一种解决方案,并不推荐大家使用。实际上,Glide 给我们提供了专门的 API 来添加和取消图片变换,想要解决这个问题只需要使用如下代码即可:

1
2
3
4
Glide.with(this)
.load(url)
.dontTransform()
.into(imageView);

可以看到,这里调用了一个 dontTransform() 方法,表示让 Glide 在加载图片的过程中不进行图片变换,这样刚才调用的 applyCenterCrop()、applyFitCenter() 就统统无效了。

现在我们重新运行一下代码,效果如下图所示:

这样图片就只会占据半个屏幕的宽度了,说明我们的代码奏效了。

但是使用 dontTransform() 方法存在着一个问题,就是调用这个方法之后,所有的图片变换操作就全部失效了,那如果我有一些图片变换操作是必须要执行的该怎么办呢?不用担心,总归是有办法的,这种情况下我们只需要借助 override() 方法强制将图片尺寸指定成原始大小就可以了,代码如下所示:

1
2
3
4
Glide.with(this)
.load(url)
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(imageView);

通过 override() 方法将图片的宽和高都指定成 Target.SIZE_ORIGINAL,问题同样被解决了。程序的最终运行结果和上图是完全一样的。

由此我们可以看出,之所以会出现这个问题,和 Glide 的图片变换功能是撇不开关系的。那么也是通过这个问题,我们对 Glide 的图片变换有了一个最基本的认识。接下来,就让我们正式开始进入本篇文章的正题吧。

图片变换的基本用法

顾名思义,图片变换的意思就是说,Glide 从加载了原始图片到最终展示给用户之前,又进行了一些变换处理,从而能够实现一些更加丰富的图片效果,如图片圆角化、圆形化、模糊化等等。

添加图片变换的用法非常简单,我们只需要调用 transform() 方法,并将想要执行的图片变换操作作为参数传入 transform() 方法即可,如下所示:

1
2
3
4
Glide.with(this)
.load(url)
.transform(...)
.into(imageView);

至于具体要进行什么样的图片变换操作,这个通常都是需要我们自己来写的。不过 Glide 已经内置了两种图片变换操作,我们可以直接拿来使用,一个是 CenterCrop,一个是 FitCenter。

但这两种内置的图片变换操作其实都不需要使用 transform() 方法,Glide 为了方便我们使用直接提供了现成的 API:

1
2
3
4
5
6
7
8
9
Glide.with(this)
.load(url)
.centerCrop()
.into(imageView);
Glide.with(this)
.load(url)
.fitCenter()
.into(imageView);

当然,centerCrop() 和 fitCenter() 方法其实也只是对 transform() 方法进行了一层封装而已,它们背后的源码仍然还是借助 transform() 方法来实现的,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class DrawableRequestBuilder<ModelType>
extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
implements BitmapOptions, DrawableOptions {
...
/**
* Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.
*
* @see #fitCenter()
* @see #transform(BitmapTransformation...)
* @see #bitmapTransform(Transformation[])
* @see #transform(Transformation[])
*
* @return This request builder.
*/
@SuppressWarnings("unchecked")
public DrawableRequestBuilder<ModelType> centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
/**
* Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.
*
* @see #centerCrop()
* @see #transform(BitmapTransformation...)
* @see #bitmapTransform(Transformation[])
* @see #transform(Transformation[])
*
* @return This request builder.
*/
@SuppressWarnings("unchecked")
public DrawableRequestBuilder<ModelType> fitCenter() {
return transform(glide.getDrawableFitCenter());
}
...
}

那么这两种内置的图片变换操作到底能实现什么样的效果呢?FitCenter 的效果其实刚才我们已经见识过了,就是会将图片按照原始的长宽比充满全屏。那么 CenterCrop 又是什么样的效果呢?我们来动手试一下就知道了。

为了让效果更加明显,这里我就不使用百度首页的 Logo 图了,而是换成必应首页的一张美图。在不应用任何图片变换的情况下,使用 Glide 加载必应这张图片效果如下所示。

现在我们添加一个 CenterCrop 的图片变换操作,代码如下:

1
2
3
4
5
String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";
Glide.with(this)
.load(url)
.centerCrop()
.into(imageView);

重新运行一下程序并点击加载图片按钮,效果如下图所示。

可以看到,现在展示的图片是对原图的中心区域进行裁剪后得到的图片。

另外,centerCrop() 方法还可以配合 override() 方法来实现更加丰富的效果,比如指定图片裁剪的比例:

1
2
3
4
5
6
String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";
Glide.with(this)
.load(url)
.override(500, 500)
.centerCrop()
.into(imageView);

可以看到,这里我们将图片的尺寸设定为 500 * 500 像素,那么裁剪的比例也就变成 1:1 了,现在重新运行一下程序,效果如下图所示。

这样我们就把 Glide 内置的图片变换接口的用法都掌握了。不过不得不说,Glide 内置的图片变换接口功能十分单一且有限,完全没有办法满足我们平时的开发需求。因此,掌握自定义图片变换功能就显得尤为重要了。

不过,在正式开始学习自定义图片变换功能之前,我们先来探究一下 CenterCrop 这种图片变换的源码,理解了它的源码我们再来进行自定义图片变换就能更加得心应手了。

源码分析

那么就话不多说,我们直接打开 CenterCrop 类来看一下它的源码吧,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class CenterCrop extends BitmapTransformation {
public CenterCrop(Context context) {
super(context);
}
public CenterCrop(BitmapPool bitmapPool) {
super(bitmapPool);
}
// Bitmap doesn't implement equals, so == and .equals are equivalent here.
@SuppressWarnings("PMD.CompareObjectsWithEquals")
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null
? toTransform.getConfig() : Bitmap.Config.ARGB_8888);
Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
toReuse.recycle();
}
return transformed;
}
@Override
public String getId() {
return "CenterCrop.com.bumptech.glide.load.resource.bitmap";
}
}

这段代码并不长,但是我还是要划下重点,这样大家看起来的时候会更加轻松。

首先,CenterCrop 是继承自 BitmapTransformation 的,这个是重中之重,因为整个图片变换功能都是建立在这个继承结构基础上的。

接下来 CenterCrop 中最重要的就是 transform() 方法,其他的方法我们可以暂时忽略。transform() 方法中有四个参数,每一个都很重要,我们来一一解读下。第一个参数 pool,这个是 Glide 中的一个 Bitmap 缓存池,用于对 Bitmap 对象进行重用,否则每次图片变换都重新创建 Bitmap 对象将会非常消耗内存。第二个参数 toTransform,这个是原始图片的 Bitmap 对象,我们就是要对它来进行图片变换。第三和第四个参数比较简单,分别代表图片变换后的宽度和高度,其实也就是 override() 方法中传入的宽和高的值了。

下面我们来看一下 transform() 方法的细节,首先第一行就从 Bitmap 缓存池中尝试获取一个可重用的 Bitmap 对象,然后把这个对象连同 toTransform、outWidth、outHeight 参数一起传入到了 TransformationUtils.centerCrop() 方法当中。那么我们就跟进去来看一下这个方法的源码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public final class TransformationUtils {
...
public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {
if (toCrop == null) {
return null;
} else if (toCrop.getWidth() == width && toCrop.getHeight() == height) {
return toCrop;
}
// From ImageView/Bitmap.createScaledBitmap.
final float scale;
float dx = 0, dy = 0;
Matrix m = new Matrix();
if (toCrop.getWidth() * height > width * toCrop.getHeight()) {
scale = (float) height / (float) toCrop.getHeight();
dx = (width - toCrop.getWidth() * scale) * 0.5f;
} else {
scale = (float) width / (float) toCrop.getWidth();
dy = (height - toCrop.getHeight() * scale) * 0.5f;
}
m.setScale(scale, scale);
m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
final Bitmap result;
if (recycled != null) {
result = recycled;
} else {
result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));
}
// We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
TransformationUtils.setAlpha(toCrop, result);
Canvas canvas = new Canvas(result);
Paint paint = new Paint(PAINT_FLAGS);
canvas.drawBitmap(toCrop, m, paint);
return result;
}
...
}

这段代码就是整个图片变换功能的核心代码了。可以看到,第 5-9 行主要是先做了一些校验,如果原图为空,或者原图的尺寸和目标裁剪尺寸相同,那么就放弃裁剪。接下来第 11-22 行是通过数学计算来算出画布的缩放的比例以及偏移值。第 24-29 行是判断缓存池中取出的 Bitmap 对象是否为空,如果不为空就可以直接使用,如果为空则要创建一个新的 Bitmap 对象。第 32 行是将原图 Bitmap 对象的 alpha 值复制到裁剪 Bitmap 对象上面。最后第 34-37 行是裁剪 Bitmap 对象进行绘制,并将最终的结果进行返回。全部的逻辑就是这样,总体来说还是比较简单的,可能也就是数学计算那边需要稍微动下脑筋。

那么现在得到了裁剪后的 Bitmap 对象,我们再回到 CenterCrop 当中,你会看到,在最终返回这个 Bitmap 对象之前,还会尝试将复用的 Bitmap 对象重新放回到缓存池当中,以便下次继续使用。

好的,这样我们就将 CenterCrop 图片变换的工作原理完整地分析了一遍,FitCenter 的源码也是基本类似的,这里就不再重复分析了。了解了这些内容之后,接下来我们就可以开始学习自定义图片变换功能了。

自定义图片变换

Glide 给我们定制好了一个图片变换的框架,大致的流程是我们可以获取到原始的图片,然后对图片进行变换,再将变换完成后的图片返回给 Glide,最终由 Glide 将图片显示出来。理论上,在对图片进行变换这个步骤中我们可以进行任何的操作,你想对图片怎么样都可以。包括圆角化、圆形化、黑白化、模糊化等等,甚至你将原图片完全替换成另外一张图都是可以的。

但是这里显然我不可能向大家演示所有图片变换的可能,图片变换的可能性也是无限的。因此这里我们就选择一种常用的图片变换效果来进行自定义吧——对图片进行圆形化变换。

图片圆形化的功能现在在手机应用中非常常见,比如手机 QQ 就会将用户的头像进行圆形化变换,从而使得界面变得更加好看。

自定义图片变换功能的实现逻辑比较固定,我们刚才看过 CenterCrop 的源码之后,相信你已经基本了解整个自定义的过程了。其实就是自定义一个类让它继承自 BitmapTransformation ,然后重写 transform() 方法,并在这里去实现具体的图片变换逻辑就可以了。一个空的图片变换实现大概如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CircleCrop extends BitmapTransformation {
public CircleCrop(Context context) {
super(context);
}
public CircleCrop(BitmapPool bitmapPool) {
super(bitmapPool);
}
@Override
public String getId() {
return "com.example.glidetest.CircleCrop";
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return null;
}
}

这里有一点需要注意,就是 getId() 方法中要求返回一个唯一的字符串来作为 id,以和其他的图片变换做区分。通常情况下,我们直接返回当前类的完整类名就可以了。

另外,这里我们选择继承 BitmapTransformation 还有一个限制,就是只能对静态图进行图片变换。当然,这已经足够覆盖日常 95% 以上的开发需求了。如果你有特殊的需求要对 GIF 图进行图片变换,那就得去自己实现 Transformation 接口才可以了。不过这个就非常复杂了,不在我们今天的讨论范围。

好了,那么我们继续实现对图片进行圆形化变换的功能,接下来只需要在 transform() 方法中去做具体的逻辑实现就可以了,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class CircleCrop extends BitmapTransformation {
public CircleCrop(Context context) {
super(context);
}
public CircleCrop(BitmapPool bitmapPool) {
super(bitmapPool);
}
@Override
public String getId() {
return "com.example.glidetest.CircleCrop";
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());
final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
final Bitmap result;
if (toReuse != null) {
result = toReuse;
} else {
result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
}
int dx = (toTransform.getWidth() - diameter) / 2;
int dy = (toTransform.getHeight() - diameter) / 2;
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP,
BitmapShader.TileMode.CLAMP);
if (dx != 0 || dy != 0) {
Matrix matrix = new Matrix();
matrix.setTranslate(-dx, -dy);
shader.setLocalMatrix(matrix);
}
paint.setShader(shader);
paint.setAntiAlias(true);
float radius = diameter / 2f;
canvas.drawCircle(radius, radius, radius, paint);
if (toReuse != null && !pool.put(toReuse)) {
toReuse.recycle();
}
return result;
}
}

下面我来对 transform() 方法中的逻辑做下简单的解释。首先第 18 行先算出原图宽度和高度中较小的值,因为对图片进行圆形化变换肯定要以较小的那个值作为直径来进行裁剪。第 20-26 行则和刚才一样,从 Bitmap 缓存池中尝试获取一个 Bitmap 对象来进行重用,如果没有可重用的 Bitmap 对象的话就创建一个。第 28-41 行是具体进行圆形化变换的部分,这里算出了画布的偏移值,并且根据刚才得到的直径算出半径来进行画圆。最后,尝试将复用的 Bitmap 对象重新放回到缓存池当中,并将圆形化变换后的 Bitmap 对象进行返回。

这样,一个自定义图片变换的功能就写好了,那么现在我们就来尝试使用一下它吧。使用方法非常简单,刚才已经介绍过了,就是把这个自定义图片变换的实例传入到 transform() 方法中即可,如下所示:

1
2
3
4
Glide.with(this)
.load(url)
.transform(new CircleCrop(this))
.into(imageView);

现在我们重新运行一下程序,效果如下图所示。

更多图片变换功能

虽说 Glide 的图片变换功能框架已经很强大了,使得我们可以轻松地自定义图片变换效果,但是如果每一种图片变换都要我们自己去写还是蛮吃力的。事实上,确实也没有必要完全靠自己去实现各种各样的图片变换效果,因为大多数的图片变换都是比较通用的,各个项目会用到的效果都差不多,我们每一个都自己去重新实现无异于重复造轮子。

也正是因此,网上出现了很多 Glide 的图片变换开源库,其中做的最出色的应该要数 glide-transformations 这个库了。它实现了很多通用的图片变换效果,如裁剪变换、颜色变换、模糊变换等等,使得我们可以非常轻松地进行各种各样的图片变换。

glide-transformations 的项目主页地址是 https://github.com/wasabeef/glide-transformations

下面我们就来体验一下这个库的强大功能吧。首先需要将这个库引入到我们的项目当中,在 app/build.gradle 文件当中添加如下依赖:

1
2
3
dependencies {
compile 'jp.wasabeef:glide-transformations:2.0.2'
}

现在如果我想对图片进行模糊化处理,那么就可以使用 glide-transformations 库中的 BlurTransformation 这个类,代码如下所示:

1
2
3
4
Glide.with(this)
.load(url)
.bitmapTransform(new BlurTransformation(this))
.into(imageView);

注意这里我们调用的是 bitmapTransform() 方法而不是 transform() 方法,因为 glide-transformations 库都是专门针对静态图片变换来进行设计的。现在重新运行一下程度,效果如下图所示。

没错,我们就这样轻松地实现模糊化的效果了。

接下来我们再试一下图片黑白化的效果,使用的是 GrayscaleTransformation 这个类,代码如下所示:

1
2
3
4
Glide.with(this)
.load(url)
.bitmapTransform(new GrayscaleTransformation(this))
.into(imageView);

现在重新运行一下程度,效果如下图所示。

而且我们还可以将多个图片变换效果组合在一起使用,比如同时执行模糊化和黑白化的变换:

1
2
3
4
Glide.with(this)
.load(url)
.bitmapTransform(new BlurTransformation(this), new GrayscaleTransformation(this))
.into(imageView);

可以看到,同时执行多种图片变换的时候,只需要将它们都传入到 bitmapTransform() 方法中即可。现在重新运行一下程序,效果如下图所示。

当然,这些只是 glide-transformations 库的一小部分功能而已,更多的图片变换效果你可以到它的 GitHub 项目主页去学习,所有变换的用法都是这么简单哦。